#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
定时任务：更新中奖状态和缓存字段

业务流程：
1. 筛选当日开奖彩种且未开奖(status='pending')的模拟记录
2. 检查对应期号是否已开奖，执行中奖计算
3. 更新模拟记录状态（status, prize_amount, winning_level等）
4. 【重要】如果记录属于追号计划，立即同步更新追号计划表的缓存字段：
   - total_prize: 累计奖金（聚合所有中奖记录的奖金）
   - actual_investment: 实际已投入金额（聚合所有已插入记录的金额，包括pending状态）
   - profit: 盈亏金额（total_prize - actual_investment）
   - period_details: 期号详情JSON（用于前端展示）
   - current_period_count: 当前已插入期数（等于模拟记录表中该追号计划的记录总数）
   - last_inserted_period: 最后插入的期号（按period_index排序获取最大的期号）
   - is_stopped: 是否已停止（中奖即停模式且已中奖）
   - status: 计划状态（stopped/completed/active）
   - last_check_time: 最后检查时间
5. ⚠️ 测试模式：Redis缓存已禁用

支持参数：可按彩种(lottery_type)、期号(issue_number)等参数筛选

建议：每10-30分钟执行一次
crontab示例：每10分钟执行一次
0,10,20,30,40,50 * * * * cd /Users/ly/Desktop/lottery && python3 lottery-script/update_winning_status.py
"""

import os
import sys
import json
import time
import argparse
from datetime import datetime
from pathlib import Path
from typing import Any
from dotenv import load_dotenv

# 加载环境变量 - 从脚本同级目录加载 .env 文件
env_path = Path(__file__).parent / '.env'
load_dotenv(env_path)

try:
    import pymysql
except ImportError:
    print("错误: 缺少必需的依赖包 pymysql")
    print("请运行: pip install pymysql")
    sys.exit(1)

# 🔥 测试阶段：Redis为可选依赖
try:
    import redis
except ImportError:
    redis = None
    print("⚠️  警告: Redis模块未安装，缓存功能将被禁用")


class WinningStatusUpdater:
    """中奖状态更新器"""

    # 等级优先级映射（数字越小优先级越高）
    PRIZE_LEVEL_PRIORITY = {
        '一等奖': 1, '二等奖': 2, '三等奖': 3, '四等奖': 4, '五等奖': 5,
        '六等奖': 6, '七等奖': 7, '八等奖': 8, '九等奖': 9, '十等奖': 10,
        '直选': 1, '组三': 2, '组六': 3
    }

    def __init__(self, lottery_type=None, issue_number=None):
        """
        初始化更新器

        参数:
            lottery_type: 彩种类型（可选），如 ssq, dlt, fc3d, pl3, pl5, qlc, qxc, kl8
            issue_number: 期号（可选），支持5位或7位格式，如 25001 或 2025001
        """
        # 从环境变量读取数据库配置（使用 python-dotenv 标准方式）
        self.config = {
            'DB_HOST': os.getenv('DB_HOST', 'localhost'),
            'DB_DATABASE': os.getenv('DB_NAME', 'lottery'),
            'DB_USERNAME': os.getenv('DB_USER', 'root'),
            'DB_PASSWORD': os.getenv('DB_PASSWORD', ''),
            'REDIS_HOST': os.getenv('REDIS_HOST', 'localhost'),
            'REDIS_PORT': int(os.getenv('REDIS_PORT', 6379))
        }

        # 筛选参数
        self.lottery_type = lottery_type
        self.issue_number = issue_number

        # 今日开奖彩种列表（场景一时使用）
        self.today_lotteries = None

        # 连接数据库
        self.db = None
        self._connect_database()

        # 🔥 测试阶段：禁用Redis缓存
        self.redis_client = None
        # self._connect_redis()  # 已禁用

        # 统计信息（测试阶段：移除缓存相关统计）
        self.stats = {
            'sim_checked': 0,
            'sim_updated': 0,
            'sim_won': 0,
            'chase_checked': 0,
            'chase_updated': 0,
            'chase_won': 0
        }

    def _connect_database(self):
        """连接MySQL数据库"""
        try:
            self.db = pymysql.connect(
                host=self.config['DB_HOST'],
                user=self.config['DB_USERNAME'],
                password=self.config['DB_PASSWORD'],
                database=self.config['DB_DATABASE'],
                charset='utf8mb4',
                cursorclass=pymysql.cursors.DictCursor,
                connect_timeout=10,
                read_timeout=30,
                write_timeout=30,
                autocommit=False
            )
            print(f"✓ 数据库连接成功: {self.config['DB_HOST']}/{self.config['DB_DATABASE']}")
        except Exception as e:
            print(f"✗ 数据库连接失败: {e}")
            sys.exit(1)
    
    def _connect_redis(self):
        """连接Redis"""
        try:
            self.redis_client = redis.Redis(
                host=self.config['REDIS_HOST'],
                port=int(self.config['REDIS_PORT']),
                decode_responses=True
            )
            # 测试连接
            self.redis_client.ping()
            print(f"✓ Redis连接成功: {self.config['REDIS_HOST']}:{self.config['REDIS_PORT']}")
        except Exception as e:
            print(f"✗ Redis连接失败: {e}")
            sys.exit(1)
    
    def _get_lottery_schedule(self):
        """
        获取彩种开奖周期配置
        
        返回:
            dict: {彩种代码: [开奖星期列表], ...}
                 星期格式：0=周日, 1=周一, 2=周二, ..., 6=周六
        """
        return {
            'ssq': [2, 4, 0],  # 双色球：周二、周四、周日
            'dlt': [1, 3, 6],  # 大乐透：周一、周三、周六
            'fc3d': [0, 1, 2, 3, 4, 5, 6],  # 福彩3D：每天
            'pl3': [0, 1, 2, 3, 4, 5, 6],  # 排列3：每天
            'pl5': [0, 1, 2, 3, 4, 5, 6],  # 排列5：每天
            'qlc': [1, 3, 5],  # 七乐彩：周一、周三、周五
            'qxc': [2, 5, 0],  # 七星彩：周二、周五、周日
            'kl8': [0, 1, 2, 3, 4, 5, 6],  # 快乐8：每天
        }
    
    def _get_lottery_name(self, lottery_type):
        """
        获取彩种中文名称
        
        参数:
            lottery_type: 彩种代码
            
        返回:
            str: 彩种中文名称
        """
        lottery_names = {
            'ssq': '双色球',
            'dlt': '大乐透',
            'fc3d': '福彩3D',
            'pl3': '排列3',
            'pl5': '排列5',
            'qlc': '七乐彩',
            'qxc': '七星彩',
            'kl8': '快乐8'
        }
        return lottery_names.get(lottery_type, lottery_type)
    
    def _get_weekday_name(self, weekday):
        """
        获取星期的中文名称
        
        参数:
            weekday: 星期数字 (0=周日, 1=周一, ..., 6=周六)
            
        返回:
            str: 星期中文名称
        """
        weekday_names = {
            0: '周日', 1: '周一', 2: '周二', 3: '周三',
            4: '周四', 5: '周五', 6: '周六'
        }
        return weekday_names.get(weekday, f'周{weekday}')
    
    def _get_today_draw_lotteries(self, target_date=None):
        """
        获取指定日期（默认今天）开奖的彩种列表
        
        参数:
            target_date: 目标日期字符串 'YYYY-MM-DD'，默认为今天
            
        返回:
            list: 今日开奖的彩种代码列表
        """
        from datetime import datetime
        
        # 获取目标日期
        if target_date:
            target_dt = datetime.strptime(target_date, '%Y-%m-%d')
        else:
            target_dt = datetime.now()
        
        date_str = target_dt.strftime('%Y-%m-%d')
        weekday = target_dt.weekday()
        # 转换为0=周日的格式
        weekday_sunday_base = (weekday + 1) % 7
        
        # 获取开奖周期配置
        schedules = self._get_lottery_schedule()
        
        # 筛选今日开奖的彩种
        draw_lotteries = []
        for lottery_type, schedule in schedules.items():
            if weekday_sunday_base in schedule:
                # 检查是否为节假日
                if not self._is_holiday(date_str, lottery_type):
                    draw_lotteries.append(lottery_type)
        
        return draw_lotteries
    
    def _is_lottery_draw_today(self, lottery_type, target_date=None):
        """
        判断指定彩种今天是否开奖
        
        参数:
            lottery_type: 彩种代码
            target_date: 目标日期字符串 'YYYY-MM-DD'，默认为今天
            
        返回:
            tuple: (是否开奖, 说明信息)
        """
        today_lotteries = self._get_today_draw_lotteries(target_date)
        
        if lottery_type in today_lotteries:
            return (True, f"{self._get_lottery_name(lottery_type)}今日开奖")
        else:
            # 获取该彩种的开奖周期
            schedules = self._get_lottery_schedule()
            schedule = schedules.get(lottery_type, [])
            schedule_names = [self._get_weekday_name(d) for d in schedule]
            schedule_str = '、'.join(schedule_names)
            
            from datetime import datetime
            target_dt = datetime.strptime(target_date, '%Y-%m-%d') if target_date else datetime.now()
            weekday = target_dt.weekday()
            weekday_sunday_base = (weekday + 1) % 7
            today_name = self._get_weekday_name(weekday_sunday_base)
            
            return (False, f"今天是{today_name}，{self._get_lottery_name(lottery_type)}不开奖（开奖日：{schedule_str}）")

    def _expand_dantuo(self, dan, tuo, select_count):
        """
        展开胆拖投注为所有单注组合

        参数:
            dan: 胆码列表（必选号码）
            tuo: 拖码列表（可选号码）
            select_count: 需要选择的总数

        返回:
            所有组合列表
        """
        from itertools import combinations

        dan_count = len(dan)
        tuo_select = select_count - dan_count

        # ✅ 修复：胆码数量不能超过总选择数（违反胆拖定义）
        if dan_count > select_count:
            print(f"  ⚠️  胆拖投注无效：胆码数量({dan_count})超过总选择数({select_count})")
            return []

        # ✅ 如果胆码数量刚好等于需要的数量，直接返回
        if tuo_select == 0:
            return [list[Any](dan)]

        # ✅ 验证：拖码数量必须足够
        if tuo_select > len(tuo):
            print(f"  ⚠️  胆拖投注无效：拖码数量({len(tuo)})不足，需要{tuo_select}个")
            return []

        # ✅ 正常展开：胆码 + 拖码组合
        result = []
        for tuo_combo in combinations(tuo, tuo_select):
            result.append(list(dan) + list(tuo_combo))

        return result

    def _expand_positioning(self, position_numbers):
        """
        展开定位复式投注（排列5/七星彩）

        参数:
            position_numbers: [[位1号码], [位2号码], ...]

        返回:
            所有组合列表
        """
        from itertools import product

        result = []
        for combo in product(*position_numbers):
            result.append(list(combo))

        return result

    def _expand_fushi(self, numbers, select_count):
        """
        展开普通复式投注

        参数:
            numbers: 号码列表
            select_count: 需要选择的数量

        返回:
            所有组合列表
        """
        from itertools import combinations

        if len(numbers) < select_count:
            return []

        result = []
        for combo in combinations(numbers, select_count):
            result.append(list(combo))

        return result

    def _expand_complex_bet(self, lottery_type, bet_type, selected_numbers, bet_count):
        """
        智能展开复杂投注（胆拖、定位、复式）

        参数:
            lottery_type: 彩种类型
            bet_type: 投注类型
            selected_numbers: 原始选号
            bet_count: 预期注数

        返回:
            展开后的单注列表 [{red: [...], blue: [...]}, ...]
        """
        result = []

        try:
            red = selected_numbers.get('red', [])
            blue = selected_numbers.get('blue', [])

            # 检测是否为胆拖格式
            if isinstance(red, dict) and 'dan' in red and 'tuo' in red:
                # 胆拖投注
                if lottery_type == 'ssq':
                    # 双色球：红球6选，蓝球1选
                    red_combos = self._expand_dantuo(red['dan'], red['tuo'], 6)

                    if isinstance(blue, dict) and 'dan' in blue and 'tuo' in blue:
                        # 双色球蓝球：dan和tuo合并为候选蓝球，每个独立成注
                        all_blues = list(blue['dan']) + list(blue.get('tuo', []))
                        blue_combos = [[b] for b in all_blues]
                    elif isinstance(blue, list):
                        blue_combos = [[b] for b in blue]
                    else:
                        blue_combos = []

                    for r in red_combos:
                        for b in blue_combos:
                            result.append({'red': r, 'blue': b})

                elif lottery_type == 'dlt':
                    # 大乐透：前区5选，后区2选
                    red_combos = self._expand_dantuo(red['dan'], red['tuo'], 5)

                    if isinstance(blue, dict) and 'dan' in blue and 'tuo' in blue:
                        blue_combos = self._expand_dantuo(blue['dan'], blue['tuo'], 2)
                    elif isinstance(blue, list):
                        blue_combos = list(self._expand_fushi(blue, 2))
                    else:
                        blue_combos = []

                    for r in red_combos:
                        for b in blue_combos:
                            result.append({'red': r, 'blue': b})

            # 检测是否为定位复式格式（排列5/七星彩）
            elif isinstance(red, list) and len(red) > 0 and isinstance(red[0], list):
                # 定位复式
                red_combos = self._expand_positioning(red)

                if isinstance(blue, list) and len(blue) > 0:
                    for r in red_combos:
                        result.append({'red': r, 'blue': blue})
                else:
                    for r in red_combos:
                        result.append({'red': r, 'blue': []})

            # 普通复式（号码数量超过单注要求）
            elif isinstance(red, list) and isinstance(blue, list):
                if lottery_type == 'ssq' and len(red) > 6:
                    # 双色球复式
                    red_combos = self._expand_fushi(red, 6)
                    blue_combos = [[b] for b in blue]

                    for r in red_combos:
                        for b in blue_combos:
                            result.append({'red': r, 'blue': b})

                elif lottery_type == 'dlt' and (len(red) > 5 or len(blue) > 2):
                    # 大乐透复式
                    if len(red) >= 5:
                        red_combos = self._expand_fushi(red, 5)
                    else:
                        red_combos = [red]

                    if len(blue) >= 2:
                        blue_combos = self._expand_fushi(blue, 2)
                    else:
                        blue_combos = [blue]

                    for r in red_combos:
                        for b in blue_combos:
                            result.append({'red': r, 'blue': b})

                elif lottery_type == 'qlc' and len(red) > 7:
                    # 七乐彩复式
                    red_combos = self._expand_fushi(red, 7)
                    for r in red_combos:
                        result.append({'red': r, 'blue': []})

                elif lottery_type == 'kl8' and len(red) > 10:
                    # 快乐8复式（假设选10个号）
                    red_combos = self._expand_fushi(red, 10)
                    for r in red_combos:
                        result.append({'red': r, 'blue': []})
                
                # 🔥 新增：排列3/福彩3D 组六/组三复式
                elif lottery_type in ['fc3d', 'pl3']:
                    if bet_type.startswith('zuliu'):
                        # 组六复式：从选中的号码中选3个不重复的号
                        # 例如：选[0,1,2,3,4,5,6,7]，组合成所有组六
                        if len(red) >= 3:
                            red_combos = self._expand_fushi(red, 3)
                            for r in red_combos:
                                result.append({'red': r, 'blue': []})
                    elif bet_type.startswith('zusan'):
                        # 组三复式：选择2个不同的号码（一个重复出现2次）
                        # 例如：选[0,1,2,3]，可以组成 001,002,003,011,012,013,...
                        if len(red) >= 2:
                            red_combos = self._expand_fushi(red, 2)
                            for r in red_combos:
                                result.append({'red': r, 'blue': []})

            # 🔥 排列3/福彩3D 特殊格式：组三单式（repeat + normal）
            elif lottery_type in ['fc3d', 'pl3'] and isinstance(red, dict):
                if 'repeat' in red and 'normal' in red:
                    # 组三特殊格式：{'repeat': [1,2,8,9], 'normal': [1,2,3,4,5,6,7,8,9]}
                    # repeat: 可能重复的号码（AAB中的A）
                    # normal: 所有可能的号码（AAB中的B）
                    # 组合：每个repeat号码可以作为重复号，配合normal中的其他号码
                    repeat_nums = red.get('repeat', [])
                    normal_nums = red.get('normal', [])
                    
                    for repeat_num in repeat_nums:
                        # 每个重复号配合所有normal号码（排除自己）
                        for normal_num in normal_nums:
                            if str(repeat_num) != str(normal_num):
                                # 组三格式：两个不同的号码
                                result.append({'red': [repeat_num, normal_num], 'blue': []})
                    
                    if result:
                        print(f"  📋 展开组三特殊格式：repeat={len(repeat_nums)}个 × normal={len(normal_nums)}个 = {len(result)}注")

        except Exception as e:
            print(f"  ⚠️  展开复杂投注失败: {e}")
            import traceback
            traceback.print_exc()

        return result

    def _check_winning(self, lottery_type, bet_type, selected_numbers, winning_numbers, bet_count):
        """
        检查是否中奖

        参数:
            lottery_type: 彩种类型 (ssq, dlt, fc3d, pl3, pl5, qlc, qxc, kl8)
            bet_type: 投注类型 (fushi, zhixuan, zusan, zuliu等)
            selected_numbers: 选号数据 [{red: [...], blue: [...]}, ...]
            winning_numbers: 开奖号码 {red: [...], blue: [...]}
            bet_count: 注数

        返回:
            {winning: bool, prize: float, level: str}
        """
        if not selected_numbers:
            return {'winning': False, 'prize': 0, 'level': ''}

        # 🔧 修复：确保selected_numbers是列表格式
        if isinstance(selected_numbers, dict):
            selected_numbers = [selected_numbers]
        elif not isinstance(selected_numbers, list):
            return {'winning': False, 'prize': 0, 'level': ''}

        # 🔥 关键修复：智能检测并展开复杂投注（胆拖、定位、复式）
        # 如果只有1注但bet_count>1，说明是未拆分的复杂投注
        expanded_success = False
        if len(selected_numbers) == 1 and bet_count > 1:
            expanded = self._expand_complex_bet(lottery_type, bet_type, selected_numbers[0], bet_count)
            if expanded:
                selected_numbers = expanded
                expanded_success = True
                print(f"  📋 展开复杂投注：1注 → {len(selected_numbers)}注")

                # 验证展开后的注数是否匹配
                if len(selected_numbers) != bet_count:
                    print(f"  ⚠️  警告：展开后注数({len(selected_numbers)}) != 记录({bet_count})，可能存在计算误差")
            else:
                # 展开失败，警告用户
                print(f"  ⚠️  数据不一致：复式投注未展开（实际1注 vs 记录{bet_count}注）")
                print(f"      彩种:{lottery_type}, 类型:{bet_type}, 选号:{selected_numbers}")

        # ✅ 验证：如果不是"1注需要展开"的情况，检查数据一致性
        elif len(selected_numbers) != bet_count and bet_count > 0:
            print(f"  ⚠️  数据异常：实际{len(selected_numbers)}注 vs 记录{bet_count}注")

        winning_red = winning_numbers.get('red', [])
        winning_blue = winning_numbers.get('blue', [])

        # 🔧 修复：复式投注需要检查所有注，累加所有中奖注的奖金
        total_prize = 0
        best_level = ''
        winning_count = 0

        for bet in selected_numbers:
            selected_red = bet.get('red', [])
            selected_blue = bet.get('blue', [])
            
            # 根据彩种调用对应的检测方法
            if lottery_type in ['fc3d', 'pl3']:
                result = self._check_3d(bet_type, selected_red, winning_red, bet_count)
            elif lottery_type == 'pl5':
                result = self._check_pl5(bet_type, selected_red, winning_red, bet_count)
            elif lottery_type == 'ssq':
                result = self._check_ssq(selected_red, selected_blue, winning_red, winning_blue, bet_count)
            elif lottery_type == 'dlt':
                result = self._check_dlt(selected_red, selected_blue, winning_red, winning_blue, bet_count)
            elif lottery_type == 'qlc':
                result = self._check_qlc(selected_red, selected_blue, winning_red, winning_blue, bet_count)
            elif lottery_type == 'qxc':
                result = self._check_qxc(selected_red, selected_blue, winning_red, winning_blue, bet_count)
            elif lottery_type == 'kl8':
                result = self._check_kl8(selected_red, winning_red, bet_count)
            else:
                result = {'winning': False, 'prize': 0, 'level': ''}
            
            # 累加所有中奖注的奖金，记录最高等级
            if result['winning']:
                total_prize += result['prize']
                winning_count += 1
                # 保留等级最高的（使用优先级映射比较，数字越小等级越高）
                current_priority = self.PRIZE_LEVEL_PRIORITY.get(result['level'], 999)
                best_priority = self.PRIZE_LEVEL_PRIORITY.get(best_level, 999) if best_level else 999
                if current_priority < best_priority:
                    best_level = result['level']
        
        if winning_count > 0:
            return {'winning': True, 'prize': total_prize, 'level': best_level}
        else:
            return {'winning': False, 'prize': 0, 'level': ''}
    
    def _check_ssq(self, selected_red, selected_blue, winning_red, winning_blue, bet_count):
        """双色球中奖判断
        
        中奖规则：
        一等奖：6红+1蓝
        二等奖：6红+0蓝
        三等奖：5红+1蓝
        四等奖：5红+0蓝 或 4红+1蓝
        五等奖：4红+0蓝 或 3红+1蓝
        六等奖：0红+1蓝（仅蓝球）
        """
        # 🔧 修复：统一容错处理
        selected_red = [str(x).zfill(2) for x in selected_red]
        selected_blue = [str(x).zfill(2) for x in (selected_blue if isinstance(selected_blue, list) else [selected_blue])]
        winning_red = [str(x).zfill(2) for x in winning_red]
        winning_blue = [str(x).zfill(2) for x in (winning_blue if isinstance(winning_blue, list) else [winning_blue])]

        red_match = len(set(selected_red) & set(winning_red))
        blue_match = len(set(selected_blue) & set(winning_blue))
        
        prize_map = {
            '一等奖': 5000000,
            '二等奖': 150000,
            '三等奖': 3000,
            '四等奖': 200,
            '五等奖': 10,
            '六等奖': 5
        }
        
        # 一等奖：6红+1蓝
        if red_match == 6 and blue_match == 1:
            return {'winning': True, 'prize': prize_map['一等奖'], 'level': '一等奖'}
        # 二等奖：6红+0蓝
        elif red_match == 6 and blue_match == 0:
            return {'winning': True, 'prize': prize_map['二等奖'], 'level': '二等奖'}
        # 三等奖：5红+1蓝
        elif red_match == 5 and blue_match == 1:
            return {'winning': True, 'prize': prize_map['三等奖'], 'level': '三等奖'}
        # 四等奖：5红+0蓝 或 4红+1蓝
        elif (red_match == 5 and blue_match == 0) or (red_match == 4 and blue_match == 1):
            return {'winning': True, 'prize': prize_map['四等奖'], 'level': '四等奖'}
        # 五等奖：4红+0蓝 或 3红+1蓝
        elif (red_match == 4 and blue_match == 0) or (red_match == 3 and blue_match == 1):
            return {'winning': True, 'prize': prize_map['五等奖'], 'level': '五等奖'}
        # ✅ 修复：六等奖：0红+1蓝（仅蓝球）
        elif red_match == 0 and blue_match == 1:
            return {'winning': True, 'prize': prize_map['六等奖'], 'level': '六等奖'}
        
        return {'winning': False, 'prize': 0, 'level': ''}
    
    def _check_dlt(self, selected_red, selected_blue, winning_red, winning_blue, bet_count):
        """大乐透中奖判断
        
        中奖规则：
        一等奖：前5+后2
        二等奖：前5+后1
        三等奖：前5+后0
        四等奖：前4+后2
        五等奖：前4+后1
        六等奖：前3+后2 或 前4+后0
        七等奖：前4+后0（已被六等奖覆盖，实际不单独判断）
        八等奖：前3+后1 或 前2+后2
        九等奖：前3+后0 或 前1+后2 或 前2+后1 或 前0+后2
        """
        # 🔧 修复：增加数据验证和容错
        try:
            if not all(isinstance(x, list) for x in [selected_red, selected_blue, winning_red, winning_blue]):
                return {'winning': False, 'prize': 0, 'level': ''}

            if len(selected_red) < 5 or len(selected_blue) < 2:
                return {'winning': False, 'prize': 0, 'level': ''}

            selected_red = [str(x).zfill(2) for x in selected_red]
            selected_blue = [str(x).zfill(2) for x in selected_blue]
            winning_red = [str(x).zfill(2) for x in winning_red]
            winning_blue = [str(x).zfill(2) for x in winning_blue]
        except (TypeError, ValueError, IndexError) as e:
            print(f"  大乐透数据格式错误: {e}")
            return {'winning': False, 'prize': 0, 'level': ''}

        red_match = len(set(selected_red) & set(winning_red))
        blue_match = len(set(selected_blue) & set(winning_blue))
        
        prize_map = {
            '一等奖': 10000000, '二等奖': 500000, '三等奖': 10000,
            '四等奖': 3000, '五等奖': 300, '六等奖': 200,
            '七等奖': 100, '八等奖': 15, '九等奖': 5
        }
        
        # 一等奖：前5+后2
        if red_match == 5 and blue_match == 2:
            return {'winning': True, 'prize': prize_map['一等奖'], 'level': '一等奖'}
        # 二等奖：前5+后1
        elif red_match == 5 and blue_match == 1:
            return {'winning': True, 'prize': prize_map['二等奖'], 'level': '二等奖'}
        # 三等奖：前5+后0
        elif red_match == 5 and blue_match == 0:
            return {'winning': True, 'prize': prize_map['三等奖'], 'level': '三等奖'}
        # 四等奖：前4+后2
        elif red_match == 4 and blue_match == 2:
            return {'winning': True, 'prize': prize_map['四等奖'], 'level': '四等奖'}
        # 五等奖：前4+后1
        elif red_match == 4 and blue_match == 1:
            return {'winning': True, 'prize': prize_map['五等奖'], 'level': '五等奖'}
        # 六等奖：前3+后2 或 前4+后0
        elif (red_match == 3 and blue_match == 2) or (red_match == 4 and blue_match == 0):
            return {'winning': True, 'prize': prize_map['六等奖'], 'level': '六等奖'}
        # ✅ 修复：八等奖：前3+后1 或 前2+后2
        elif (red_match == 3 and blue_match == 1) or (red_match == 2 and blue_match == 2):
            return {'winning': True, 'prize': prize_map['八等奖'], 'level': '八等奖'}
        # ✅ 修复：九等奖：前3+后0 或 前1+后2 或 前2+后1 或 前0+后2
        elif (red_match == 3 and blue_match == 0) or \
             (red_match == 1 and blue_match == 2) or \
             (red_match == 2 and blue_match == 1) or \
             (red_match == 0 and blue_match == 2):
            return {'winning': True, 'prize': prize_map['九等奖'], 'level': '九等奖'}
        
        return {'winning': False, 'prize': 0, 'level': ''}
    
    def _check_3d(self, bet_type, selected_red, winning_red, bet_count):
        """福彩3D/排列三中奖判断"""
        if len(winning_red) < 3:
            return {'winning': False, 'prize': 0, 'level': ''}
        
        # 🔧 统一数据类型：转换为字符串
        selected_red = [str(x) for x in selected_red]
        winning_red = [str(x) for x in winning_red]
        
        prize_map = {
            'zhixuan': 1040, 'zhixuan_dingwei': 1040,
            'zusan': 346, 'zusan_fushi': 346, 'zusan_wanneng': 346,
            'zuliu': 173, 'zuliu_fushi': 173, 'zuliu_wanneng': 173
        }
        
        # 直选类型
        if bet_type in ['zhixuan', 'zhixuan_dingwei']:
            if not isinstance(selected_red, list) or len(selected_red) != 3:
                return {'winning': False, 'prize': 0, 'level': ''}
            
            # 位置必须完全一致
            if selected_red[0] == winning_red[0] and selected_red[1] == winning_red[1] and selected_red[2] == winning_red[2]:
                return {'winning': True, 'prize': prize_map['zhixuan'], 'level': '直选'}
        
        # 组三/组六类型
        elif bet_type.startswith('zusan') or bet_type.startswith('zuliu'):
            # 判断开奖号码是否符合组三/组六规则
            winning_counts = {}
            for num in winning_red:
                winning_counts[num] = winning_counts.get(num, 0) + 1
            
            is_zusan = (len(winning_counts) == 2)  # 组三：AAB或ABB
            is_zuliu = (len(winning_counts) == 3)  # 组六：ABC
            
            if bet_type.startswith('zusan'):
                if not is_zusan:
                    return {'winning': False, 'prize': 0, 'level': ''}
                
                # 组三：检查用户选号是否包含开奖号码的所有不同数字
                winning_unique = list(set(winning_red))
                all_included = all(num in selected_red for num in winning_unique)
                
                if all_included:
                    return {'winning': True, 'prize': prize_map['zusan'], 'level': '组三'}
            
            elif bet_type.startswith('zuliu'):
                if not is_zuliu:
                    return {'winning': False, 'prize': 0, 'level': ''}
                
                # 组六：检查用户选号是否包含开奖号码的所有3个数字
                all_included = all(num in selected_red for num in winning_red)
                
                if all_included:
                    return {'winning': True, 'prize': prize_map['zuliu'], 'level': '组六'}
        
        return {'winning': False, 'prize': 0, 'level': ''}
    
    def _check_pl5(self, bet_type, selected_red, winning_red, bet_count):
        """排列5中奖判断"""
        if len(winning_red) < 5:
            return {'winning': False, 'prize': 0, 'level': ''}
        
        # 🔧 统一数据类型：转换为字符串
        selected_red = [str(x) for x in selected_red]
        winning_red = [str(x) for x in winning_red]
        
        # 排列5只有直选，一等奖10万元
        if bet_type in ['zhixuan', 'zhixuan_dingwei']:
            if not isinstance(selected_red, list) or len(selected_red) != 5:
                return {'winning': False, 'prize': 0, 'level': ''}
            
            # 位置必须完全一致
            if all(selected_red[i] == winning_red[i] for i in range(5)):
                return {'winning': True, 'prize': 100000, 'level': '一等奖'}
        
        return {'winning': False, 'prize': 0, 'level': ''}
    
    def _check_qlc(self, selected_red, selected_blue, winning_red, winning_blue, bet_count):
        """七乐彩中奖判断"""
        # 🔧 修复：增加数据验证
        if not isinstance(selected_red, list) or not isinstance(winning_red, list):
            return {'winning': False, 'prize': 0, 'level': ''}

        selected_red = [str(x).zfill(2) for x in selected_red]
        winning_red = [str(x).zfill(2) for x in winning_red]

        basic_match = len(set(selected_red) & set(winning_red))
        special_match = 0
        if isinstance(winning_blue, list) and len(winning_blue) > 0:
            special_match = len(set(selected_red) & set([str(x).zfill(2) for x in winning_blue]))
        
        prize_map = {
            '一等奖': 5000000, '二等奖': 100000, '三等奖': 3000,
            '四等奖': 200, '五等奖': 50, '六等奖': 10, '七等奖': 5
        }
        
        if basic_match == 7:
            return {'winning': True, 'prize': prize_map['一等奖'], 'level': '一等奖'}
        elif basic_match == 6 and special_match == 1:
            return {'winning': True, 'prize': prize_map['二等奖'], 'level': '二等奖'}
        elif basic_match == 6:
            return {'winning': True, 'prize': prize_map['三等奖'], 'level': '三等奖'}
        elif basic_match == 5 and special_match == 1:
            return {'winning': True, 'prize': prize_map['四等奖'], 'level': '四等奖'}
        elif basic_match == 5:
            return {'winning': True, 'prize': prize_map['五等奖'], 'level': '五等奖'}
        elif basic_match == 4 and special_match == 1:
            return {'winning': True, 'prize': prize_map['六等奖'], 'level': '六等奖'}
        elif basic_match == 4:
            return {'winning': True, 'prize': prize_map['七等奖'], 'level': '七等奖'}
        
        return {'winning': False, 'prize': 0, 'level': ''}
    
    def _check_qxc(self, selected_red, selected_blue, winning_red, winning_blue, bet_count):
        """七星彩中奖判断"""
        # 🔧 修复：先验证数据长度
        if len(winning_red) < 6 or len(winning_blue) < 1:
            return {'winning': False, 'prize': 0, 'level': ''}
        if len(selected_red) < 6 or len(selected_blue) < 1:
            return {'winning': False, 'prize': 0, 'level': ''}

        # 🔧 统一数据类型：转换为字符串
        selected_red = [str(x) for x in selected_red]
        selected_blue = [str(x) for x in selected_blue]
        winning_red = [str(x) for x in winning_red]
        winning_blue = [str(x) for x in winning_blue]

        prize_map = {
            '一等奖': 5000000, '二等奖': 100000, '三等奖': 3000,
            '四等奖': 500, '五等奖': 30, '六等奖': 5
        }

        # 七星彩：前6位按位置匹配，第7位也按位置匹配
        red_matches = sum(1 for i in range(6) if selected_red[i] == winning_red[i])
        blue_match = (selected_blue[0] == winning_blue[0]) if len(selected_blue) > 0 and len(winning_blue) > 0 else False
        
        if red_matches == 6 and blue_match:
            return {'winning': True, 'prize': prize_map['一等奖'], 'level': '一等奖'}
        elif red_matches == 6:
            return {'winning': True, 'prize': prize_map['二等奖'], 'level': '二等奖'}
        elif red_matches == 5 and blue_match:
            return {'winning': True, 'prize': prize_map['三等奖'], 'level': '三等奖'}
        elif red_matches == 5:
            return {'winning': True, 'prize': prize_map['四等奖'], 'level': '四等奖'}
        elif red_matches == 4 and blue_match:
            return {'winning': True, 'prize': prize_map['五等奖'], 'level': '五等奖'}
        elif red_matches == 4 or blue_match:
            return {'winning': True, 'prize': prize_map['六等奖'], 'level': '六等奖'}
        
        return {'winning': False, 'prize': 0, 'level': ''}
    
    def _check_kl8(self, selected_red, winning_red, bet_count):
        """快乐8中奖判断"""
        # 🔧 修复：增加数据验证
        if not isinstance(selected_red, list) or not isinstance(winning_red, list):
            return {'winning': False, 'prize': 0, 'level': ''}

        selected_red = [str(x).zfill(2) for x in selected_red]
        winning_red = [str(x).zfill(2) for x in winning_red]

        match_count = len(set(selected_red) & set(winning_red))
        select_count = len(selected_red)
        
        prize_map = {
            '5-5': 4500, '5-4': 50, '5-3': 5,
            '6-6': 20000, '6-5': 200, '6-4': 10, '6-3': 3,
            '7-7': 50000, '7-6': 1800, '7-5': 30, '7-4': 5, '7-0': 5,
            '8-8': 500000, '8-7': 8000, '8-6': 300, '8-5': 20, '8-4': 4, '8-0': 4,
            '9-9': 1000000, '9-8': 30000, '9-7': 3000, '9-6': 100, '9-5': 8, '9-4': 3, '9-0': 3,
            '10-10': 5000000, '10-9': 50000, '10-8': 3000, '10-7': 200, '10-6': 20, '10-5': 5, '10-0': 5
        }
        
        key = f"{select_count}-{match_count}"
        if key in prize_map:
            return {'winning': True, 'prize': prize_map[key], 'level': f"选{select_count}中{match_count}"}
        
        return {'winning': False, 'prize': 0, 'level': ''}
    
    def update_simulation_records(self):
        """更新模拟记录的中奖状态（含同步更新追号计划）"""
        print("\n【步骤1】更新模拟记录中奖状态（含同步更新追号计划）")
        
        # 🔥 新增：根据参数判断处理模式
        from datetime import datetime
        today_str = datetime.now().strftime('%Y-%m-%d')
        weekday = datetime.now().weekday()
        weekday_sunday_base = (weekday + 1) % 7
        today_name = self._get_weekday_name(weekday_sunday_base)
        
        # 场景判断
        if self.issue_number:
            # 场景三：指定了期号（必然也指定了彩种）- 强制处理模式
            print(f"  🔒 强制处理模式")
            print(f"  彩种: {self._get_lottery_name(self.lottery_type)}")
            print(f"  期号: {self.issue_number}")
            print(f"  说明: 跳过开奖日期验证，直接按条件查询")
        elif self.lottery_type:
            # 场景二：只指定彩种 - 需验证是否今日开奖
            is_draw, message = self._is_lottery_draw_today(self.lottery_type)
            print(f"  📅 今天是{today_name}")
            print(f"  彩种: {self._get_lottery_name(self.lottery_type)}")
            print(f"  验证: {message}")
            
            if not is_draw:
                print(f"\n  ⚠️  {self._get_lottery_name(self.lottery_type)}今日不开奖，跳过处理")
                print(f"  💡 如需强制处理，请同时指定期号参数：")
                print(f"     python3 update_winning_status.py --lottery-type {self.lottery_type} --issue-number <期号>")
                return
            print(f"  ✓ 继续处理{self._get_lottery_name(self.lottery_type)}的待开奖记录")
        else:
            # 场景一：未指定任何参数 - 处理今日所有开奖彩种
            today_lotteries = self._get_today_draw_lotteries()
            if not today_lotteries:
                print(f"  📅 今天是{today_name}")
                print(f"  ⚠️  今日无彩种开奖（可能为节假日），脚本退出")
                return
            
            lottery_names = [self._get_lottery_name(lt) for lt in today_lotteries]
            print(f"  📅 今天是{today_name}")
            print(f"  ✓ 今日开奖彩种: {', '.join(lottery_names)} (共{len(today_lotteries)}个)")
            
            # 🔥 关键修复：保存今日开奖彩种列表，供后续SQL查询使用
            self.today_lotteries = today_lotteries
        
        print("-" * 40)

        try:
            with self.db.cursor() as cursor:
                # 构建动态SQL查询 - 只查询已开奖的记录
                sql_conditions = ["us.status = 'pending'"]
                sql_params = []

                # 添加彩种筛选
                if self.lottery_type:
                    sql_conditions.append("us.lottery_type = %s")
                    sql_params.append(self.lottery_type)
                elif self.today_lotteries:
                    # 🔥 场景一：使用今日开奖彩种列表过滤
                    placeholders = ','.join(['%s'] * len(self.today_lotteries))
                    sql_conditions.append(f"us.lottery_type IN ({placeholders})")
                    sql_params.extend(self.today_lotteries)

                # 添加期号筛选（兼容5位和7位）
                if self.issue_number:
                    period = str(self.issue_number)  #2025315
                    period5= period;
                    period7= period;
                    if len(period) == 5:
                        period7 = '20' + period 
                    elif len(period) == 7:
                        period5 = period[-5:]         
                   
                    sql_conditions.append("(us.issue_number = %s OR us.issue_number = %s)")
                    sql_params.extend([period5, period7])
                else:
                    # 未指定期号时，只处理当日开奖的彩种
                    sql_conditions.append("DATE(lr.draw_date) = CURDATE()")

                # 使用INNER JOIN确保只查询已开奖的记录，并直接获取开奖号码
                # 🔥 修复：期号匹配需要双向兼容5位和7位格式
                sql = f"""
                    SELECT us.id, us.user_id, us.lottery_type, us.type, us.issue_number,
                           us.selected_numbers, us.bet_count, us.multiple, us.chase_id,
                           lr.period as winning_period, lr.all_numbers as winning_all_numbers
                    FROM user_simulations us
                    INNER JOIN lottery_results lr ON lr.lottery_type = us.lottery_type
                        AND (
                            lr.period = us.issue_number 
                            OR lr.period = CONCAT('20', us.issue_number)
                            OR CONCAT('20', lr.period) = us.issue_number
                            OR RIGHT(lr.period, 5) = us.issue_number
                            OR RIGHT(us.issue_number, 5) = lr.period
                        )
                    WHERE {' AND '.join(sql_conditions)}
                    ORDER BY us.created_at DESC
                    LIMIT 200
                """
                cursor.execute(sql, sql_params)
                pending_records = cursor.fetchall()

                # 🔥 关键修复：追踪需要更新的追号计划ID
                chase_plans_to_update = set()

                for record in pending_records:
                    try:
                        self.stats['sim_checked'] += 1

                        # 🔧 修复：解析开奖号码，增加异常处理
                        try:
                            all_numbers = record['winning_all_numbers']
                            if isinstance(all_numbers, str):
                                all_numbers = json.loads(all_numbers)
                        except (json.JSONDecodeError, TypeError) as e:
                            print(f"  解析开奖号码失败 ID:{record['id']}: {e}")
                            continue

                        winning_numbers = {'red': [], 'blue': []}
                        if isinstance(all_numbers, list) and len(all_numbers) > 0:
                            if all_numbers[0]:
                                winning_numbers['red'] = all_numbers[0].split()
                            if len(all_numbers) > 1 and all_numbers[1] and all_numbers[1] != 'None':
                                winning_numbers['blue'] = all_numbers[1].split()

                        # 🔧 修复：解析用户选号，增加异常处理
                        try:
                            selected_numbers = record['selected_numbers']
                            if isinstance(selected_numbers, str):
                                selected_numbers = json.loads(selected_numbers)
                        except (json.JSONDecodeError, TypeError) as e:
                            print(f"  解析用户选号失败 ID:{record['id']}: {e}")
                            continue

                        # 执行中奖检测
                        bet_type = record.get('type', 'fushi')
                        # 🔥 修复：容错处理 bet_count 为 None 或 0 的情况
                        bet_count = int(record.get('bet_count') or 1)
                        if bet_count <= 0:
                            print(f"  ⚠️  ID:{record['id']} bet_count 无效({bet_count})，跳过")
                            continue

                        winning_result = self._check_winning(
                            record['lottery_type'],
                            bet_type,
                            selected_numbers,
                            winning_numbers,
                            bet_count
                        )

                        # 🔥 修复：计算总奖金（考虑倍数，容错处理）
                        multiple = int(record.get('multiple') or 1)
                        if multiple <= 0:
                            multiple = 1
                        total_prize = winning_result['prize'] * multiple

                        # 更新数据库
                        update_sql = """
                            UPDATE user_simulations
                            SET status = %s,
                                winning_numbers = %s,
                                prize_amount = %s,
                                winning_level = %s,
                                last_check_time = NOW(),
                                updated_at = NOW()
                            WHERE id = %s
                        """

                        new_status = 'winning' if winning_result['winning'] else 'not_winning'
                        cursor.execute(update_sql, (
                            new_status,
                            json.dumps(winning_numbers, ensure_ascii=False),
                            total_prize,
                            winning_result['level'] if winning_result['winning'] else None,
                            record['id']
                        ))

                        # ✅ 数据库更新成功后，更新统计
                        self.stats['sim_updated'] += 1

                        # 🔥 优化：输出详细的中奖/未中奖日志
                        lottery_name = self._get_lottery_name(record['lottery_type'])
                        
                        # 格式化投注号码（支持胆拖格式）
                        def format_numbers(nums):
                            def format_ball(ball):
                                """格式化单个球区（支持列表、胆拖字典等）"""
                                if isinstance(ball, list):
                                    if len(ball) == 0:
                                        return ""
                                    # 所有元素都是数字
                                    if all(isinstance(x, (int, str)) for x in ball):
                                        return ','.join([str(n).zfill(2) for n in ball])
                                    return str(ball)
                                elif isinstance(ball, dict):
                                    # 胆拖格式：{'dan': [...], 'tuo': [...]}
                                    if 'dan' in ball and 'tuo' in ball:
                                        dan = ball.get('dan', [])
                                        tuo = ball.get('tuo', [])
                                        dan_str = ','.join([str(n).zfill(2) for n in dan]) if dan else ""
                                        tuo_str = ','.join([str(n).zfill(2) for n in tuo]) if tuo else ""
                                        if dan_str and tuo_str:
                                            return f"胆:{dan_str} 拖:{tuo_str}"
                                        elif dan_str:
                                            return f"胆:{dan_str}"
                                        elif tuo_str:
                                            return f"拖:{tuo_str}"
                                    return str(ball)
                                return str(ball)
                            
                            if isinstance(nums, list):
                                if len(nums) == 0:
                                    return ""
                                # 多注
                                if len(nums) > 1 and isinstance(nums[0], dict):
                                    return f"{len(nums)}注"
                                # 单注
                                if len(nums) == 1 and isinstance(nums[0], dict):
                                    red = nums[0].get('red', [])
                                    blue = nums[0].get('blue', [])
                                    red_str = format_ball(red)
                                    blue_str = format_ball(blue)
                                    if red_str and blue_str:
                                        return f"红:{red_str} 蓝:{blue_str}"
                                    elif red_str:
                                        return red_str
                                    elif blue_str:
                                        return blue_str
                                # 直接是号码列表
                                return ','.join([str(n).zfill(2) for n in nums])
                            elif isinstance(nums, dict):
                                red = nums.get('red', [])
                                blue = nums.get('blue', [])
                                red_str = format_ball(red)
                                blue_str = format_ball(blue)
                                if red_str and blue_str:
                                    return f"红:{red_str} 蓝:{blue_str}"
                                elif red_str:
                                    return red_str
                                elif blue_str:
                                    return blue_str
                            return str(nums)
                        
                        selected_str = format_numbers(selected_numbers)
                        
                        # 格式化开奖号码
                        winning_red_str = ','.join([str(n).zfill(2) for n in winning_numbers.get('red', [])])
                        winning_blue_str = ','.join([str(n).zfill(2) for n in winning_numbers.get('blue', [])])
                        if winning_blue_str:
                            winning_str = f"红:{winning_red_str} 蓝:{winning_blue_str}"
                        else:
                            winning_str = winning_red_str
                        
                        if winning_result['winning']:
                            self.stats['sim_won'] += 1
                            # 中奖日志
                            print(f"  🎉 【中奖】")
                            print(f"     用户ID: {record['user_id']} | 彩种: {lottery_name} | 期号: {record['issue_number']}")
                            print(f"     投注号码: {selected_str}")
                            print(f"     开奖号码: {winning_str}")
                            print(f"     中奖等级: {winning_result['level']} | 倍数: {multiple}倍 | 奖金: {int(total_prize)}元")
                            if record.get('chase_id'):
                                print(f"     追号计划ID: {record['chase_id']}")
                        else:
                            # 未中奖日志
                            print(f"  ❌ 【未中奖】")
                            print(f"     用户ID: {record['user_id']} | 彩种: {lottery_name} | 期号: {record['issue_number']}")
                            print(f"     投注号码: {selected_str}")
                            print(f"     开奖号码: {winning_str}")
                            if record.get('chase_id'):
                                print(f"     追号计划ID: {record['chase_id']}")

                        # 🔥 关键修复：如果该记录属于追号计划，标记需要更新计划表
                        if record.get('chase_id'):
                            chase_plans_to_update.add(record['chase_id'])

                    except Exception as e:
                        print(f"  检查模拟记录 ID:{record['id']} 失败: {e}")
                        import traceback
                        print(f"  错误详情: {traceback.format_exc()}")

                # 🔥 关键修复：同步更新受影响的追号计划表
                # ⚠️ 不要在这里 commit，要等追号计划也更新完再一起提交，保证事务一致性
                if chase_plans_to_update:
                    print(f"\n  📊 发现 {len(chase_plans_to_update)} 个追号计划需要同步更新")
                    for chase_id in chase_plans_to_update:
                        try:
                            # 🔥 优化：先处理自动插入，再统一更新一次（避免重复更新）
                            # 1️⃣ 先处理中奖即停模式的自动插入下一期
                            self._handle_stop_on_win_next_period(cursor, chase_id)
                            
                            # 2️⃣ 再更新追号计划表（包含所有最新数据：开奖结果+新插入记录）
                            self._update_chase_cache_from_simulations(cursor, chase_id, verbose=True)
                        except Exception as e:
                            print(f"  ⚠️  更新追号计划 ID:{chase_id} 失败: {e}")
                            import traceback
                            traceback.print_exc()
                
                # ✅ 统一提交：模拟记录 + 追号计划 + 新插入记录，保证事务一致性
                self.db.commit()

        except Exception as e:
            print(f"  更新模拟记录失败: {e}")
            self.db.rollback()
        
        print(f"\n  📊 处理统计:")
        print(f"     检查记录数: {self.stats['sim_checked']} 条")
        print(f"     更新记录数: {self.stats['sim_updated']} 条")
        if self.stats['sim_won'] > 0:
            print(f"     🎉 中奖记录: {self.stats['sim_won']} 条")

    def _handle_stop_on_win_next_period(self, cursor, chase_id):
        """
        处理中奖即停模式的下一期自动插入逻辑
        
        业务规则：
        - stop_on_win=1: 每期开奖后，如果未中奖且追号未结束，自动插入下一期
        - stop_on_win=0: API创建时已插入所有期数，脚本无需处理
        
        参数:
            cursor: 数据库游标
            chase_id: 追号计划ID
        """
        try:
            # 1️⃣ 查询追号计划基本信息
            chase_sql = """
                SELECT id, user_id, lottery_type, type as bet_type, 
                       selected_numbers, amount, multiple, 
                       chase_count, stop_on_win, status,
                       issue_number, bet_count
                FROM user_chase_numbers
                WHERE id = %s
            """
            cursor.execute(chase_sql, (chase_id,))
            chase_plan = cursor.fetchone()
            
            if not chase_plan:
                return
            
            # 2️⃣ 只处理 stop_on_win=1 的计划
            if not chase_plan['stop_on_win']:
                return
            
            # 3️⃣ 检查计划状态：只处理 active 状态
            if chase_plan['status'] not in ['active', 'pending']:
                return
            
            # 4️⃣ 查询已插入的模拟记录统计
            stats_sql = """
                SELECT 
                    COUNT(*) as total_inserted,
                    MAX(period_index) as last_period_index,
                    SUM(CASE WHEN status = 'winning' THEN 1 ELSE 0 END) as win_count
                FROM user_simulations
                WHERE chase_id = %s
            """
            cursor.execute(stats_sql, (chase_id,))
            stats = cursor.fetchone()
            
            total_inserted = stats['total_inserted'] or 0
            last_period_index = stats['last_period_index'] or 0
            win_count = stats['win_count'] or 0
            
            # 🔥 关键修复：获取最后一期的期号（按 period_index 排序，避免字符串比较）
            last_issue_sql = """
                SELECT issue_number
                FROM user_simulations
                WHERE chase_id = %s
                ORDER BY period_index DESC
                LIMIT 1
            """
            cursor.execute(last_issue_sql, (chase_id,))
            last_issue_result = cursor.fetchone()
            last_issue = last_issue_result['issue_number'] if last_issue_result else None
            
            # 5️⃣ 判断是否需要插入下一期
            # 条件：未中奖 + 未达到追号期数
            if win_count > 0:
                print(f"    🏆 追号计划 ID:{chase_id} 已中奖，停止插入")
                return
            
            if total_inserted >= chase_plan['chase_count']:
                print(f"    ✅ 追号计划 ID:{chase_id} 已达到总期数({chase_plan['chase_count']})")
                return
            
            # 🔥 修复逻辑：检查最后一期是否已开奖，而不是检查是否有 pending
            # 中奖即停模式：插入一期后等该期开奖，未中奖再插入下一期
            if last_issue:
                last_period_status_sql = """
                    SELECT status
                    FROM user_simulations
                    WHERE chase_id = %s AND period_index = %s
                """
                cursor.execute(last_period_status_sql, (chase_id, last_period_index))
                last_status = cursor.fetchone()
                
                if last_status and last_status['status'] == 'pending':
                    print(f"    ⏳ 追号计划 ID:{chase_id} 最后一期(第{last_period_index}期)还未开奖，暂不插入")
                    return
            
            # 6️⃣ 计算下一期期号和索引
            # period_index 表示追号的第几期，应该连续递增
            # 使用 MAX(period_index) + 1，即使某期被删除也能保持顺序
            next_period_index = last_period_index + 1
            
            # 🔥 兜底逻辑：如果没有任何记录（理论上不应该发生，因为API已创建第一期）
            if total_inserted == 0:
                if chase_plan['issue_number']:
                    next_issue = str(chase_plan['issue_number'])
                    print(f"    📌 [兜底] 追号计划 ID:{chase_id} 插入第1期（起始期号: {next_issue}）")
                else:
                    print(f"    ⚠️  追号计划 ID:{chase_id} 缺少起始期号(issue_number)")
                    return
            else:
                # 正常流程：根据最后一期计算下一期期号
                next_issue = self._calculate_next_issue(
                    chase_plan['lottery_type'], 
                    last_issue
                )
                
                if not next_issue:
                    print(f"    ⚠️  追号计划 ID:{chase_id} 无法计算下一期期号")
                    return
            
            # 7️⃣ 插入下一期记录（防止重复插入）
            # 🔐 并发安全：使用 INSERT IGNORE 或先检查是否已存在
            check_sql = """
                SELECT id
                FROM user_simulations
                WHERE chase_id = %s AND period_index = %s
                FOR UPDATE
            """
            cursor.execute(check_sql, (chase_id, next_period_index))
            existing = cursor.fetchone()
            
            if existing:
                print(f"    ⏭️  追号计划 ID:{chase_id} 第{next_period_index}期已存在，跳过插入")
                return
            
            # 确认不存在后再插入
            insert_sql = """
                INSERT INTO user_simulations 
                (user_id, lottery_type, type, issue_number, selected_numbers, 
                 bet_count, amount, multiple, status, chase_id, period_index, created_at)
                VALUES (%s, %s, %s, %s, %s, %s, %s, %s, 'pending', %s, %s, NOW())
            """
            cursor.execute(insert_sql, (
                chase_plan['user_id'],
                chase_plan['lottery_type'],
                chase_plan['bet_type'],
                next_issue,
                chase_plan['selected_numbers'],
                chase_plan['bet_count'],
                chase_plan['amount'],
                chase_plan['multiple'],
                chase_id,
                next_period_index
            ))
            
            # 🔥 优化：输出详细的插入日志
            lottery_name = self._get_lottery_name(chase_plan['lottery_type'])
            print(f"    ➕ 【自动插入下一期】")
            print(f"       用户ID: {chase_plan['user_id']} | 彩种: {lottery_name} | 追号计划ID: {chase_id}")
            print(f"       期号: {next_issue} (第{next_period_index}期/{chase_plan['chase_count']}期)")
            print(f"       金额: {chase_plan['amount']}元 × {chase_plan['multiple']}倍 = {chase_plan['amount'] * chase_plan['multiple']}元")
            
            # 📌 说明：不在这里更新追号计划表，交由外层统一更新（避免重复）
            
        except Exception as e:
            print(f"    ❌ 处理中奖即停逻辑失败 ID:{chase_id}: {e}")
            import traceback
            print(f"    详情: {traceback.format_exc()}")
    
    def _calculate_next_issue(self, lottery_type, current_issue):
        """
        计算下一期期号（考虑彩种日程和节假日）
        
        策略：从数据库查询下一期实际期号，而不是简单+1
        如果数据库中没有，再根据开奖日程推算
        
        参数:
            lottery_type: 彩种类型
            current_issue: 当前期号
            
        返回:
            下一期期号（字符串），失败返回None
        """
        try:
            if not current_issue:
                return None
            
            current_issue = str(current_issue)
            
            # 🔥 优化1：从数据库查询下一期实际期号（最可靠）
            next_issue_from_db = self._query_next_issue_from_db(lottery_type, current_issue)
            if next_issue_from_db:
                # ✅ 验证数据库返回的期号是否合法（检查开奖日是否为节假日）
                next_date = self._get_issue_draw_date(lottery_type, next_issue_from_db)
                if next_date and self._is_holiday(next_date, lottery_type):
                    print(f"    ⚠️  数据库期号 {next_issue_from_db} 在节假日({next_date})，重新推算")
                    # 数据不准确，改用日程推算
                    return self._calculate_next_issue_by_schedule(lottery_type, current_issue)
                return next_issue_from_db
            
            # 🔥 优化2：数据库中没有，根据彩种日程和节假日推算
            return self._calculate_next_issue_by_schedule(lottery_type, current_issue)
                
        except Exception as e:
            print(f"    计算下一期期号失败: {e}")
            return None
    
    def _query_next_issue_from_db(self, lottery_type, current_issue):
        """
        从数据库查询下一期期号
        
        参数:
            lottery_type: 彩种类型
            current_issue: 当前期号
            
        返回:
            下一期期号（5位格式），未找到返回None
        """
        try:
            with self.db.cursor() as cursor:
                # 兼容5位和7位期号
                current_issue_str = str(current_issue)
                current_7 = '20' + current_issue_str if len(current_issue_str) == 5 else current_issue_str
                current_5 = current_issue_str[-5:] if len(current_issue_str) == 7 else current_issue_str
                
                # 🔥 修复：分别查询5位和7位格式，避免数值比较混淆
                # 先查5位格式
                sql_5 = """
                    SELECT period, draw_date
                    FROM lottery_results
                    WHERE lottery_type = %s
                    AND LENGTH(period) = 5
                    AND CAST(period AS UNSIGNED) > CAST(%s AS UNSIGNED)
                    ORDER BY CAST(period AS UNSIGNED) ASC
                    LIMIT 1
                """
                cursor.execute(sql_5, (lottery_type, current_5))
                result = cursor.fetchone()
                
                # 如果5位格式没找到，再查7位格式
                if not result:
                    sql_7 = """
                        SELECT period, draw_date
                        FROM lottery_results
                        WHERE lottery_type = %s
                        AND LENGTH(period) = 7
                        AND CAST(period AS UNSIGNED) > CAST(%s AS UNSIGNED)
                        ORDER BY CAST(period AS UNSIGNED) ASC
                        LIMIT 1
                    """
                    cursor.execute(sql_7, (lottery_type, current_7))
                result = cursor.fetchone()
                
                if result:
                    next_period = str(result['period'])
                    # 统一返回5位格式
                    if len(next_period) == 7:
                        next_period = next_period[-5:]
                    print(f"    📅 从数据库获取下一期: {next_period}")
                    return next_period
                
                return None
                
        except Exception as e:
            print(f"    查询下一期失败: {e}")
            return None
    
    def _calculate_next_issue_by_schedule(self, lottery_type, current_issue):
        """
        根据彩种开奖日程推算下一期期号
        
        参数:
            lottery_type: 彩种类型
            current_issue: 当前期号
            
        返回:
            下一期期号（5位格式），失败返回None
        """
        try:
            current_issue = str(current_issue)
            
            # 🔥 修复：使用统一的开奖周期配置方法，避免重复定义
            schedules = self._get_lottery_schedule()
            schedule = schedules.get(lottery_type, [0,1,2,3,4,5,6])
            
            # 查询当前期的开奖日期
            current_date = self._get_issue_draw_date(lottery_type, current_issue)
            if not current_date:
                # 无法获取日期，简单+1（兜底）
                print(f"    ⚠️  无法获取期号 {current_issue} 的开奖日期，使用简单递增")
                return self._simple_increment_issue(current_issue)
            
            # 从当前日期查找下一个开奖日
            from datetime import datetime, timedelta
            current_dt = datetime.strptime(current_date, '%Y-%m-%d')
            
            # 搜索下一个开奖日（最多60天）
            for i in range(1, 61):
                next_dt = current_dt + timedelta(days=i)
                weekday = next_dt.weekday()
                # Python weekday: 0=周一, 转换为 0=周日 格式
                weekday_sunday_base = (weekday + 1) % 7
                
                if weekday_sunday_base in schedule:
                    # 检查是否为节假日
                    if not self._is_holiday(next_dt.strftime('%Y-%m-%d'), lottery_type):
                        # 找到下一个开奖日，计算期号
                        return self._calculate_issue_for_date(
                            lottery_type, 
                            current_issue, 
                            current_date, 
                            next_dt.strftime('%Y-%m-%d')
                        )
            
            # 找不到，兜底处理
            print(f"    ⚠️  未找到下一个开奖日，使用简单递增")
            return self._simple_increment_issue(current_issue)
            
        except Exception as e:
            print(f"    根据日程推算失败: {e}")
            return self._simple_increment_issue(current_issue)
    
    def _get_issue_draw_date(self, lottery_type, issue_number):
        """
        获取指定期号的开奖日期
        
        返回:
            日期字符串 'YYYY-MM-DD' 或 None
        """
        try:
            with self.db.cursor() as cursor:
                issue_str = str(issue_number)
                issue_7 = '20' + issue_str if len(issue_str) == 5 else issue_str
                
                sql = """
                    SELECT draw_date
                    FROM lottery_results
                    WHERE lottery_type = %s
                    AND (period = %s OR period = %s)
                    AND draw_date IS NOT NULL
                    LIMIT 1
                """
                cursor.execute(sql, (lottery_type, issue_str, issue_7))
                result = cursor.fetchone()
                
                if result and result['draw_date']:
                    draw_date = result['draw_date']
                    if isinstance(draw_date, str):
                        return draw_date[:10]
                    else:
                        return draw_date.strftime('%Y-%m-%d')
                
                return None
                
        except Exception as e:
            print(f"    获取开奖日期失败: {e}")
            return None
    
    def _is_holiday(self, date_str, lottery_type):
        """
        检查指定日期是否为节假日
        
        参数:
            date_str: 日期字符串 'YYYY-MM-DD'
            lottery_type: 彩种类型
            
        返回:
            True=节假日, False=正常开奖日
        """
        try:
            with self.db.cursor() as cursor:
                sql = """
                    SELECT id
                    FROM lottery_holidays
                    WHERE (lottery_type = %s OR lottery_type IS NULL)
                    AND %s BETWEEN start_date AND end_date
                    LIMIT 1
                """
                cursor.execute(sql, (lottery_type, date_str))
                result = cursor.fetchone()
                return result is not None
                
        except Exception as e:
            # 查询失败，保守处理：假设不是节假日
            return False
    
    def _calculate_issue_for_date(self, lottery_type, current_issue, current_date, target_date):
        """
        根据日期计算期号（处理跨年）
        
        参数:
            lottery_type: 彩种类型
            current_issue: 当前期号（5位或7位）
            current_date: 当前期开奖日期
            target_date: 目标日期
            
        返回:
            目标日期的期号（5位格式）
        """
        from datetime import datetime
        
        current_issue = str(current_issue)[-5:]  # 统一为5位
        current_year = int(current_issue[:2])
        current_seq = int(current_issue[2:])
        
        target_dt = datetime.strptime(target_date, '%Y-%m-%d')
        target_year = int(target_dt.strftime('%y'))
        
        # 判断是否跨年
        if current_year == target_year:
            # 同年：期号+1
            next_seq = current_seq + 1
            if next_seq > 999:
                print(f"    ⚠️  期号超过999，切换到新年")
                return f"{target_year:02d}001"
            return f"{target_year:02d}{next_seq:03d}"
        else:
            # 跨年：从目标年份的第1期开始
            # 查询目标年份已有的最大期号
            max_seq = self._get_max_sequence_in_year(lottery_type, target_year)
            next_seq = max_seq + 1
            return f"{target_year:02d}{next_seq:03d}"
    
    def _get_max_sequence_in_year(self, lottery_type, year):
        """
        获取指定年份已有的最大期号序号
        
        参数:
            lottery_type: 彩种类型
            year: 年份（2位，如25）
            
        返回:
            最大序号（整数）
        """
        try:
            with self.db.cursor() as cursor:
                year_prefix = f"{year:02d}"
                
                sql = """
                    SELECT MAX(CAST(SUBSTRING(period, -3) AS UNSIGNED)) as max_seq
                    FROM lottery_results
                    WHERE lottery_type = %s
                    AND (period LIKE %s OR period LIKE %s)
                """
                cursor.execute(sql, (
                    lottery_type,
                    f"{year_prefix}%",
                    f"20{year_prefix}%"
                ))
                result = cursor.fetchone()
                
                if result and result['max_seq']:
                    return int(result['max_seq'])
                
                return 0
                
        except Exception as e:
            print(f"    查询年度最大序号失败: {e}")
            return 0
    
    def _simple_increment_issue(self, current_issue):
        """
        简单递增期号（兜底方案）
        
        参数:
            current_issue: 当前期号
            
        返回:
            下一期期号
        """
        current_issue = str(current_issue)
        
        # 7位格式
        if len(current_issue) == 7:
            year = current_issue[:4]
            period = int(current_issue[4:])
            next_period = period + 1
            
            if next_period > 999:
                # 跨年
                next_year = int(year) + 1
                return f"{next_year}001"
            
            return f"{year}{next_period:03d}"
        
        # 5位格式
        elif len(current_issue) == 5:
            year = current_issue[:2]
            period = int(current_issue[2:])
            next_period = period + 1
            
            if next_period > 999:
                # 跨年
                next_year = (int(year) + 1) % 100
                return f"{next_year:02d}001"
            
            return f"{year}{next_period:03d}"
        
        return None

    def _update_chase_cache_from_simulations(self, cursor, chase_id, mode='full', verbose=True):
        """
        更新追号计划的缓存字段
        
        支持两种模式：
        1. full: 全量重新计算（从模拟记录表聚合，确保数据准确）
        2. incremental: 增量更新（性能更好，但需要确保初始数据正确）

        参数:
            cursor: 数据库游标
            chase_id: 追号计划ID
            mode: 更新模式 'full' 或 'incremental'
            verbose: 是否输出详细日志
        """
        try:
            # 1️⃣ 获取计划基本信息
            chase_sql = """
                SELECT id, amount, chase_count, stop_on_win,
                       total_prize, actual_investment, profit,
                       current_period_count
                FROM user_chase_numbers
                WHERE id = %s
            """
            cursor.execute(chase_sql, (chase_id,))
            chase_plan = cursor.fetchone()

            if not chase_plan:
                print(f"    ⚠️  追号计划 ID:{chase_id} 不存在")
                return

            # 2️⃣ 从模拟记录表聚合数据（全量重新计算，确保准确性）
            # 📌 业务说明：虽然用户描述的是增量更新，但为了数据准确性和容错性，
            #    我们仍然使用全量计算方式，这样即使有异常情况也能自动修复
            aggregate_sql = """
                SELECT
                    COUNT(*) as total_periods,
                    SUM(CASE WHEN status != 'pending' THEN 1 ELSE 0 END) as checked_periods,
                    SUM(CASE WHEN status = 'winning' THEN prize_amount ELSE 0 END) as total_prize,
                    MIN(CASE WHEN status = 'winning' THEN period_index ELSE NULL END) as first_win_period
                FROM user_simulations
                WHERE chase_id = %s
            """
            cursor.execute(aggregate_sql, (chase_id,))
            stats = cursor.fetchone()
            
            # 🔥 修复：单独查询最后一期期号（按 period_index 排序，避免字符串比较）
            last_period_sql = """
                SELECT issue_number
                FROM user_simulations
                WHERE chase_id = %s
                ORDER BY period_index DESC
                LIMIT 1
            """
            cursor.execute(last_period_sql, (chase_id,))
            last_period_result = cursor.fetchone()
            last_inserted_period = str(last_period_result['issue_number']) if last_period_result else None

            # 🔥 修复：计算actual_investment
            # 📌 业务逻辑：插入模拟记录时就已经扣款，所以应该计算所有已插入的记录（包括pending）
            # 而不是只计算已开奖的记录
            first_win_period = stats['first_win_period']
            stop_on_win = chase_plan['stop_on_win']

            if stop_on_win and first_win_period:
                # 中奖即停模式：只计算到首次中奖期（包含）
                # 🔥 修复：SUM(amount) 而不是 SUM(CASE WHEN status != 'pending' ...)
                investment_sql = """
                    SELECT SUM(amount) as actual_investment
                    FROM user_simulations
                    WHERE chase_id = %s AND period_index <= %s
                """
                cursor.execute(investment_sql, (chase_id, first_win_period))
                investment_result = cursor.fetchone()
                actual_investment = float(investment_result['actual_investment'] or 0)
            else:
                # 正常模式：计算所有已插入的记录（包括pending）
                # 🔥 修复：SUM(amount) 而不是 SUM(CASE WHEN status != 'pending' ...)
                investment_sql = """
                    SELECT SUM(amount) as actual_investment
                    FROM user_simulations
                    WHERE chase_id = %s
                """
                cursor.execute(investment_sql, (chase_id,))
                investment_result = cursor.fetchone()
                actual_investment = float(investment_result['actual_investment'] or 0)

            # 3️⃣ 获取每期详情（用于前端展示）
            detail_sql = """
                SELECT period_index, issue_number, status, prize_amount, amount as investment
                FROM user_simulations
                WHERE chase_id = %s
                ORDER BY period_index ASC
            """
            cursor.execute(detail_sql, (chase_id,))
            periods = cursor.fetchall()

            # 4️⃣ 构建period_details
            total_prize = float(stats['total_prize'] or 0)
            # actual_investment 已在上面单独计算
            total_periods = stats['total_periods']  # 已插入期数（包括pending）
            checked_periods = stats['checked_periods']  # 已开奖期数（不包括pending）
            # last_inserted_period 已在上面单独查询

            period_details = []
            stopped = False

            for period in periods:
                # 如果是中奖即停模式，且之前已经中奖，后续期标记为skipped
                if stop_on_win and first_win_period and period['period_index'] > first_win_period:
                    stopped = True

                period_info = {
                    'period': period['period_index'],
                    'issue': str(period['issue_number']),
                    'status': 'skipped' if stopped else period['status'],
                    'investment': float(period['investment']),
                    'prize': float(period['prize_amount'] or 0),
                    'is_drawn': period['status'] != 'pending'
                }
                period_details.append(period_info)

            # 5️⃣ 计算追号计划状态
            profit = total_prize - actual_investment

            # 是否已停止（中奖即停模式且已中奖）
            is_stopped = 1 if (stop_on_win and first_win_period) else 0

            # 🔥 修复：计划状态判断（注意判断顺序，先判断中奖即停）
            if is_stopped:
                plan_status = 'stopped'  # 中奖即停（优先级最高）
            elif checked_periods >= chase_plan['chase_count']:
                # ⚠️ 关键：用 chase_count（计划总期数），而不是 total_periods（已插入期数）
                # 原因：中奖即停模式下，total_periods 可能小于 chase_count
                plan_status = 'completed'  # 追完所有期
            elif total_periods >= chase_plan['chase_count']:
                # 已插入所有期，但还有未开奖的
                plan_status = 'active'  # 等待最后几期开奖
            else:
                plan_status = 'active'  # 进行中
            
            # 🔥 修复：current_period_count 业务逻辑
            # 📌 业务说明：
            # - current_period_count 表示"当前已插入期数"
            # - 非中奖即停模式：API创建时一次性插入所有期，无需更新（但为了数据一致性仍然更新）
            # - 中奖即停模式：每次插入新期时需要更新（脚本在_handle_stop_on_win_next_period中插入）
            current_period_count = total_periods  # 使用已插入期数，而不是已开奖期数

            # 6️⃣ 更新计划表缓存字段
            update_sql = """
                UPDATE user_chase_numbers
                SET total_prize = %s,
                    actual_investment = %s,
                    profit = %s,
                    period_details = %s,
                    current_period_count = %s,
                    last_inserted_period = %s,
                    is_stopped = %s,
                    status = %s,
                    last_check_time = NOW(),
                    updated_at = NOW()
                WHERE id = %s
            """
            cursor.execute(update_sql, (
                total_prize,
                actual_investment,
                profit,
                json.dumps(period_details, ensure_ascii=False),
                current_period_count,  # 🔥 修复：使用total_periods而不是checked_periods
                last_inserted_period,
                is_stopped,
                plan_status,
                chase_id
            ))

            if verbose:
                print(f"    ✅ 追号计划 ID:{chase_id} 缓存已更新")
                print(f"       累计奖金: {int(total_prize)}元 | 实际投入: {int(actual_investment)}元 | 盈亏: {int(profit):+d}元")
                print(f"       已插入期数: {current_period_count}/{chase_plan['chase_count']} | 已开奖期数: {checked_periods}")
                if last_inserted_period:
                    print(f"       最后插入期号: {last_inserted_period}")
            
            return {
                'total_prize': total_prize,
                'actual_investment': actual_investment,
                'profit': profit,
                'current_period_count': current_period_count,
                'checked_periods': checked_periods,
                'last_inserted_period': last_inserted_period
            }

        except Exception as e:
            print(f"    ❌ 更新追号计划 ID:{chase_id} 失败: {e}")
            import traceback
            print(f"    详情: {traceback.format_exc()}")
            return None

    def update_chase_records(self):
        """
        更新追号计划的缓存字段（已废弃 - 仅作为兜底工具保留）

        ⚠️ 注意：此方法已不在主流程中调用
        正常情况下，追号计划会在 update_simulation_records() 中实时同步更新
        此方法仅用于手动修复长时间未更新的数据（超过2小时）
        """
        print("\n【兜底工具】更新追号计划缓存字段")
        print("-" * 40)

        try:
            with self.db.cursor() as cursor:
                # 🔧 优化：只查询长时间未更新的计划（兜底机制）
                # 正常情况下应该在步骤1中已经更新了
                sql = """
                    SELECT DISTINCT ucn.id
                    FROM user_chase_numbers ucn
                    LEFT JOIN user_simulations us ON us.chase_id = ucn.id
                    WHERE (ucn.last_check_time IS NULL
                       OR ucn.last_check_time < DATE_SUB(NOW(), INTERVAL 2 HOUR))
                    AND ucn.status = 'active'
                    LIMIT 50
                """
                cursor.execute(sql)
                chase_records = cursor.fetchall()

                if not chase_records:
                    print("  无需更新的追号计划")
                    return

                for record in chase_records:
                    try:
                        chase_id = record['id']
                        self.stats['chase_checked'] += 1

                        # 使用聚合方法更新缓存
                        self._update_chase_cache_from_simulations(cursor, chase_id)

                        self.stats['chase_updated'] += 1

                    except Exception as e:
                        print(f"  更新追号记录 ID:{record['id']} 失败: {e}")

                self.db.commit()

        except Exception as e:
            print(f"  更新追号记录失败: {e}")
            self.db.rollback()

        print(f"  检查记录数: {self.stats['chase_checked']}")
        print(f"  更新记录数: {self.stats['chase_updated']}")

    def clear_redis_cache(self):
        """清除Redis缓存（测试阶段已禁用）"""
        # 🔥 测试阶段：不进行缓存操作
        pass
    
    def print_summary(self, duration):
        """打印统计摘要"""
        print("\n" + "=" * 60)
        print("✅ 更新完成")
        print("=" * 60)
        print(f"\n⏱  总耗时: {duration:.2f}秒")
        print(f"📅 完成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
        
        print(f"\n📊 模拟记录统计:")
        print(f"   • 检查: {self.stats['sim_checked']} 条")
        print(f"   • 更新: {self.stats['sim_updated']} 条")
        if self.stats['sim_won'] > 0:
            print(f"   • 🎉 中奖: {self.stats['sim_won']} 条")
        
        if self.stats['chase_checked'] > 0 or self.stats['chase_updated'] > 0:
            print(f"\n📋 追号计划统计:")
            print(f"   • 检查: {self.stats['chase_checked']} 条")
            print(f"   • 更新: {self.stats['chase_updated']} 条")
            if self.stats['chase_won'] > 0:
                print(f"   • 🎉 中奖: {self.stats['chase_won']} 条")
        
        print("=" * 60)
    
    def run(self):
        """执行更新任务"""
        print("=" * 40)
        print("开始更新中奖状态和缓存字段")
        print(f"时间：{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
        print("=" * 40)
        
        start_time = time.time()
        
        try:
            # 步骤1: 更新模拟记录（含同步更新追号计划）
            self.update_simulation_records()

            
            # 打印统计信息
            duration = time.time() - start_time
            self.print_summary(duration)
            
        except Exception as e:
            print(f"\n❌ 执行失败: {e}")
            import traceback
            print(f"错误堆栈:\n{traceback.format_exc()}")
            return False
        
        finally:
            # 关闭连接
            if self.db:
                self.db.close()
            if self.redis_client:
                self.redis_client.close()
        
        return True


def main():
    """主函数"""
    # 解析命令行参数
    parser = argparse.ArgumentParser(
        description='更新彩票中奖状态和追号计划缓存字段',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
使用示例：
  # 场景一：更新所有今日开奖彩种的待处理记录
  python3 update_winning_status.py

  # 场景二：只更新指定彩种（需验证是否今日开奖）
  python3 update_winning_status.py --lottery-type ssq

  # 场景三：强制更新指定彩种的指定期号（跳过日期验证）
  python3 update_winning_status.py --lottery-type ssq --issue-number 25120
  python3 update_winning_status.py --lottery-type dlt --issue-number 25050

参数规则：
  - 无参数：处理今日所有开奖彩种
  - 只指定彩种：仅处理该彩种（需今日开奖）
  - 彩种+期号：强制处理指定数据（不限日期）
  - ⚠️  不允许只指定期号，必须同时指定彩种

支持的彩种：
  ssq  : 双色球      (周二、周四、周日)
  dlt  : 大乐透      (周一、周三、周六)
  fc3d : 福彩3D      (每天)
  pl3  : 排列3       (每天)
  pl5  : 排列5       (每天)
  qlc  : 七乐彩      (周一、周三、周五)
  qxc  : 七星彩      (周二、周五、周日)
  kl8  : 快乐8       (每天)
        """
    )

    parser.add_argument(
        '--lottery-type',
        type=str,
        choices=['ssq', 'dlt', 'fc3d', 'pl3', 'pl5', 'qlc', 'qxc', 'kl8'],
        help='彩种类型（可选）'
    )

    parser.add_argument(
        '--issue-number',
        type=str,
        help='期号（可选），支持5位或7位格式，如 25001 或 2025001。⚠️ 指定期号时必须同时指定彩种'
    )

    args = parser.parse_args()

    # 🔥 新增：参数验证 - 指定期号时必须同时指定彩种
    if args.issue_number and not args.lottery_type:
        print("=" * 60)
        print("❌ 参数错误")
        print("=" * 60)
        print()
        print("问题：指定了期号参数但未指定彩种参数")
        print()
        print("原因：系统无法确定要处理哪个彩种的数据，")
        print("      因为不同彩种可能有相同的期号")
        print()
        print("解决：请同时指定 --lottery-type 和 --issue-number")
        print()
        print("示例：")
        print("  # 处理双色球25120期")
        print("  python3 update_winning_status.py --lottery-type ssq --issue-number 25120")
        print()
        print("  # 处理大乐透25050期")
        print("  python3 update_winning_status.py --lottery-type dlt --issue-number 25050")
        print()
        print("支持的彩种代码：")
        print("  ssq  : 双色球      (周二、周四、周日)")
        print("  dlt  : 大乐透      (周一、周三、周六)")
        print("  fc3d : 福彩3D      (每天)")
        print("  pl3  : 排列3       (每天)")
        print("  pl5  : 排列5       (每天)")
        print("  qlc  : 七乐彩      (周一、周三、周五)")
        print("  qxc  : 七星彩      (周二、周五、周日)")
        print("  kl8  : 快乐8       (每天)")
        print()
        print("=" * 60)
        sys.exit(1)

    # 创建更新器实例
    updater = WinningStatusUpdater(
        lottery_type=args.lottery_type,
        issue_number=args.issue_number
    )

    success = updater.run()
    sys.exit(0 if success else 1)


if __name__ == '__main__':
    main()

